﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Reflection;
using SharpShare.Configuration;

namespace SharpShare.Activation {
    public class ConfigurationExtensionFactory<T> : IExtensionFactory<T>
        where T : ConfigurationElement, IExtension {
        public ConfigurationExtensionFactory(System.Configuration.Configuration config) {
            this.Configuration = config;
        }

        public System.Configuration.Configuration Configuration { get; private set; }

        #region IExtensionFactory<T> Members

        public IEnumerable<ExtensionDescriptor<T>> GetDescriptors() {
            var extensionSections = this.ListExtensionSections(this.Configuration.RootSectionGroup);

            foreach (var section in extensionSections) {
                var attribute = section.GetType().GetCustomAttribute<ExtensionAttribute>();

                ExtensionDescriptor<T> descriptor = new ExtensionDescriptor<T>(attribute.Name, section.ElementType) {
                    Description = attribute.Description
                };

                yield return descriptor;
            }
        }

        public IEnumerable<T> ListExtensions() {
            foreach (var section in this.ListExtensionSections(this.Configuration.RootSectionGroup)) {
                foreach (var element in section) {
                    yield return (T)element;
                }
            }
        }

        public void AddExtension(T extension) {
            var section = this.ListExtensionSections(this.Configuration.RootSectionGroup)
                .Where(s => s.ElementType.IsAssignableFrom(extension.GetType()))
                .FirstOrDefault();
            if (section == null)
                throw new ArgumentException("Unable to figure out what section to put this extension in.", "extension");

            section.Add(extension);
        }

        public bool ContainsExtension(T extension) {
            return (this.ListExtensions().Contains(extension));
        }

        public void RemoveExtension(T extension) {
            var section = this.ListExtensionSections(this.Configuration.RootSectionGroup)
                .Where(s => s.ElementType.IsAssignableFrom(extension.GetType()))
                .FirstOrDefault();
            if (section == null)
                throw new ArgumentException("Unable to figure out what section to remove this extension from.", "extension");

            section.Remove(extension);
        }

        #endregion

        private IEnumerable<IExtensionConfigurationSection> ListExtensionSections(ConfigurationSectionGroup group) {
            foreach (ConfigurationSectionGroup subGroup in group.SectionGroups) {
                foreach (var subExtension in ListExtensionSections(subGroup))
                    yield return subExtension;
            }

            foreach (ConfigurationSection subSection in group.Sections) {
                Type sectionType = subSection.GetType();

                if (sectionType.GetCustomAttribute<ExtensionAttribute>() == null)
                    continue;

                IExtensionConfigurationSection extensionSection = (subSection as IExtensionConfigurationSection);
                if (extensionSection != null && typeof(T).IsAssignableFrom(extensionSection.ElementType))
                    yield return extensionSection;
            }
        }
    }
}
